package com.kmxd.ams.app.archive;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kmxd.ams.client.archive.IArchiveFieldSvc;
import com.kmxd.ams.client.archive.dto.*;
import com.kmxd.ams.client.archive.vo.ArchiveFieldListVO;
import com.kmxd.ams.client.archive.vo.ArchiveFieldVO;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.CacheConst;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.DbTypeEnum;
import com.kmxd.ams.core.common.query.QueryGen;
import com.kmxd.ams.core.config.AmsConfig;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.StrUtil;
import com.kmxd.ams.infra.archive.entity.ArchiveField;
import com.kmxd.ams.infra.archive.mapper.ArchiveFieldMapper;
import com.kmxd.ams.infra.tmpl.util.CreateTableUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.kmxd.ams.core.common.constant.Constants.*;

/**
 * 门类配置-元数据 服务实现类
 *
 * @author ZuoLG
 * @since 2022-07-15
 */
@Service
@Slf4j
@AllArgsConstructor
@CacheConfig(cacheNames = CacheConst.CACHE_PREFIX_FIELD)
public class ArchiveFieldSvcImpl extends ServiceImpl<ArchiveFieldMapper, ArchiveField>
    implements IArchiveFieldSvc {

  private ArchiveFieldMapper archiveFieldMapper;
  private JdbcTemplate jdbcTemplate;
  private AmsConfig amsConfig;
  private CreateTableUtils createTableUtils;

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public Page<ArchiveFieldListVO> selectPage(Page reqPage, ArchiveFieldQueryDTO req) {
    QueryWrapper<ArchiveField> wrapper = QueryGen.init(new QueryWrapper<ArchiveField>(), req);
    if (cn.hutool.core.util.StrUtil.isNotBlank(req.getField())) {
      wrapper.orderBy(
          cn.hutool.core.util.StrUtil.isNotBlank(req.getField()),
          "ascend".equals(req.getOrder()),
          StringUtils.camelToUnderline(req.getField()));
    }
    LambdaQueryWrapper<ArchiveField> queryWrapper = wrapper.lambda();
    queryWrapper.eq(
        Objects.nonNull(req.getArchTypeId()), ArchiveField::getArchTypeId, req.getArchTypeId());
    queryWrapper.eq(
        cn.hutool.core.util.StrUtil.isNotBlank(req.getTableId()),
        ArchiveField::getTableId,
        req.getTableId());

    queryWrapper.orderBy(true, false, ArchiveField::getFieldGroup, ArchiveField::getSort);
    if (ObjectUtil.isNotEmpty(req.getDbFieldKeyList())) {
      queryWrapper.in(ArchiveField::getDbFieldKey, req.getDbFieldKeyList());
    }

    Page<ArchiveField> page = archiveFieldMapper.selectPage(reqPage, queryWrapper);
    Page<ArchiveFieldListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), ArchiveFieldListVO.class));
    return voPage;
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectList(ArchiveFieldQueryDTO req) {
    LambdaQueryWrapper<ArchiveField> queryWrapper =
        QueryGen.init(new QueryWrapper<ArchiveField>(), req).lambda();
    queryWrapper.orderBy(true, false, ArchiveField::getFieldGroup, ArchiveField::getSort);

    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @Override
  public List<ArchiveFieldListVO> selectListByOssSetting(ArchiveFieldQueryDTO req) {
    LambdaQueryWrapper<ArchiveField> queryWrapper =
        QueryGen.init(new QueryWrapper<ArchiveField>(), req).lambda();
    queryWrapper.orderBy(
        true, false, ArchiveField::getAttrUse, ArchiveField::getFieldGroup, ArchiveField::getSort);

    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    ArchiveField fieldOld = list.stream().findFirst().orElse(null);
    if (ObjectUtil.isNotEmpty(fieldOld)) {
      // 添加档案门类名称
      ArchiveField fieldArchTypeName = BeanUtil.copyProperties(fieldOld, ArchiveField.class);
      fieldArchTypeName.setId(IdWorker.getId());
      fieldArchTypeName.setDbFieldKey(ArchConst.OSS_SETTING_ARCH_TYPE_KEY);
      fieldArchTypeName.setDbFieldName(ArchConst.OSS_SETTING_ARCH_TYPE_NAME);
      list.add(fieldArchTypeName);
      // 添加全宗名称
      ArchiveField fieldFondsName = BeanUtil.copyProperties(fieldOld, ArchiveField.class);
      fieldFondsName.setId(IdWorker.getId());
      fieldFondsName.setDbFieldKey(ArchConst.OSS_SETTING_FONDS_KEY);
      fieldFondsName.setDbFieldName(ArchConst.OSS_SETTING_FONDS_NAME);
      list.add(fieldFondsName);
    }

    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @Override
  public List<ArchiveFieldListVO> selectListByExport(ArchiveFieldQueryDTO req) {
    LambdaQueryWrapper<ArchiveField> queryWrapper =
        QueryGen.init(new QueryWrapper<ArchiveField>(), req).lambda();
    queryWrapper.eq(ArchiveField::getAttrUse, Boolean.TRUE);
    queryWrapper.orderBy(
        true, false, ArchiveField::getAttrUse, ArchiveField::getFieldGroup, ArchiveField::getSort);

    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectListAll() {
    LambdaQueryWrapper<ArchiveField> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.orderBy(true, false, ArchiveField::getFieldGroup, ArchiveField::getSort);
    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectListClumns(ArchiveFieldQueryDTO req) {
    LambdaQueryWrapper<ArchiveField> queryWrapper =
        QueryGen.init(new QueryWrapper<ArchiveField>(), req).lambda();
    queryWrapper.orderBy(true, true, ArchiveField::getAttrList, ArchiveField::getSortList);
    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectListByform(ArchiveFieldQueryDTO req) {
    List<ArchiveFieldListVO> list = selectListByTableId(req.getTableId());
    List<ArchiveFieldListVO> listVO =
        list.stream()
            .filter(
                x -> {
                  if (ObjectUtil.isNotEmpty(req.getArchTypeId())) {
                    return ObjectUtil.equals(x.getArchTypeId(), req.getArchTypeId());
                  }
                  if (ObjectUtil.isNotEmpty(req.getTableId())) {
                    return ObjectUtil.equals(x.getTableId(), req.getTableId());
                  }
                  if (ObjectUtil.isNotEmpty(req.getDbFieldKeyList())) {
                    return req.getDbFieldKeyList().contains(x.getDbFieldKey());
                  }
                  return true;
                })
            .collect(Collectors.toList());
    return listVO.stream()
        .sorted(
            Comparator.comparing(ArchiveFieldListVO::getFieldGroup)
                .thenComparing(ArchiveFieldListVO::getSort))
        .collect(Collectors.toList());
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectListByIds(Set<Long> ids) {
    if (ids.isEmpty()) {
      return Lists.newArrayList();
    }
    LambdaQueryWrapper<ArchiveField> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.in(ArchiveField::getId, ids);
    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectListByTableId(String tableId) {
    if (ObjectUtil.isEmpty(tableId)) {
      return Lists.newArrayList();
    }
    LambdaQueryWrapper<ArchiveField> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.in(ArchiveField::getTableId, tableId);
    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(ArchiveFieldAddDTO req) {
    req.setDbFieldName(StrUtil.strSpecialFilter(req.getDbFieldName()));
    ArchiveField entity = BeanUtil.copyProperties(req, ArchiveField.class);
    // 根据选择类型配置动态获取字段名称
    entity.setDbFieldKey(getFieldKey(req));

    entity.setAliasEn(
        ObjectUtil.isNotEmpty(entity.getAliasEn()) ? entity.getAliasEn() : entity.getDbFieldKey());

    archiveFieldMapper.insert(entity);
    // 默认值为64，此处直接写死
    if (ObjectUtil.isNotEmpty(req.getDbLength()) && req.getDbLength() >= 64) {
      // 修改实际数据库长度
      if (cn.hutool.core.util.StrUtil.isBlank(amsConfig.getDbType())
          || cn.hutool.core.util.StrUtil.equals(
              DbTypeEnum.MYSQL.getCode(), amsConfig.getDbType())) {
        jdbcTemplate.execute(
            cn.hutool.core.util.StrUtil.format(
                "ALTER TABLE {} MODIFY {} {}({});",
                req.getTableId(),
                entity.getDbFieldKey(),
                entity.getDbType(),
                entity.getDbLength()));
      } else {
        jdbcTemplate.execute(
            cn.hutool.core.util.StrUtil.format(
                "ALTER TABLE {} MODIFY {} {};",
                req.getTableId(),
                entity.getDbFieldKey(),
                createTableUtils.getFieldLenPG(entity.getDbType(), entity.getDbLength())));
      }
    }
  }

  /**
   * 根据类型返回当前最新可用的key
   *
   * @return
   */
  private String getFieldKey(ArchiveFieldAddDTO req) {
    String fieldPrefix = getFieldPrefix(req.getDbType());
    ArchiveFieldQueryDTO fieldQueryDTO = new ArchiveFieldQueryDTO();
    fieldQueryDTO.setArchTypeId(req.getArchTypeId());
    fieldQueryDTO.setTableId(req.getTableId());
    fieldQueryDTO.setDbFieldKey(getFieldPrefix(req.getDbType()));
    List<ArchiveFieldListVO> lists = selectList(fieldQueryDTO);
    if (lists.isEmpty()) {
      return fieldPrefix + "1";
    } else {
      // 获取
      List<String> lastFields =
          lists.stream().map(ArchiveFieldListVO::getDbFieldKey).collect(Collectors.toList());
      List<String> newFields =
          CollectionUtil.subtractToList(getFieldList(req.getDbType()), lastFields);
      if (newFields.isEmpty()) {
        // 超过最大个数
        throw BizTipException.instance(ErrorCode.OVER_LIMIT_NUMBER_5, "当前可添加数据字段超过系统限制！");
      }
      return newFields.get(0);
    }
  }

  /**
   * 根据类型响应相应的扩展字段前缀
   *
   * @param dbType
   * @return
   */
  private List<String> getFieldList(String dbType) {
    String type = "ext_";
    int size = 0;
    switch (dbType) {
      case EXT_STRING_KEY:
      case EXT_STRING_KEY_LONG:
        type = type + "str";
        size = EXT_STRING_MAX;
        break;
      case EXT_INT_KEY_YEAR:
      case EXT_INT_KEY:
        type = type + "int";
        size = EXT_INT_MAX;
        break;
      case EXT_DATE_KEY:
        type = type + "date";
        size = EXT_DATE_MAX;
        break;
      default:
        break;
    }
    List<String> lists = Lists.newArrayList();
    for (int i = 1; i <= size; i++) {
      lists.add(type + i);
    }
    return lists;
  }

  /**
   * 根据类型响应相应的扩展字段前缀
   *
   * @param dbType
   * @return
   */
  private String getFieldPrefix(String dbType) {
    String type = "ext_";
    switch (dbType) {
      case EXT_STRING_KEY:
        type = type + "str";
        break;
      case EXT_INT_KEY:
        type = type + "int";
        break;
      case EXT_DATE_KEY:
        type = type + "date";
        break;
      default:
        break;
    }
    return type;
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = {Exception.class, SQLException.class})
  public void edit(ArchiveFieldEditDTO req) {
    if (cn.hutool.core.util.StrUtil.equals("id", req.getDbFieldKey())
        && !BooleanUtil.isTrue(req.getAttrUse())) {
      throw BizTipException.instance(ErrorCode.ARCHIVE_DATA_INVALIDED, "ID标识字段不允许此操作！");
    }
    req.setDbFieldName(StrUtil.strSpecialFilter(req.getDbFieldName()));
    ArchiveField entity = BeanUtil.copyProperties(req, ArchiveField.class);
    // 判断新的长度是否跟旧的相等
    ArchiveFieldVO dbFieldVO = view(req.getId());
    if (!NumberUtil.equals(dbFieldVO.getDbLength(), req.getDbLength())) {
      // 修改实际数据库长度
      if (cn.hutool.core.util.StrUtil.isBlank(amsConfig.getDbType())
          || cn.hutool.core.util.StrUtil.equals(
              DbTypeEnum.MYSQL.getCode(), amsConfig.getDbType())) {
        jdbcTemplate.execute(
            cn.hutool.core.util.StrUtil.format(
                "ALTER TABLE {} MODIFY {} {}({});",
                dbFieldVO.getTableId(),
                dbFieldVO.getDbFieldKey(),
                req.getDbType(),
                req.getDbLength()));
      } else {
        jdbcTemplate.execute(
            cn.hutool.core.util.StrUtil.format(
                "ALTER TABLE {} MODIFY {} {};",
                req.getTableId(),
                entity.getDbFieldKey(),
                createTableUtils.getFieldLenPG(entity.getDbType(), entity.getDbLength())));
      }
    }
    entity.setAliasEn(
        ObjectUtil.isNotEmpty(entity.getAliasEn()) ? entity.getAliasEn() : entity.getDbFieldKey());
    archiveFieldMapper.updateById(entity);
  }

  @CacheEvict(allEntries = true)
  @Override
  public void updateSort(ArchiveFieldEditSortDTO req) {
    List<Long> ids =
        req.getFieldSorts().stream().map(item -> item.getId()).collect(Collectors.toList());
    if (ids.isEmpty()) {
      throw BizTipException.instance(ErrorCode.ARCHIVE_DATA_INVALIDED, "操作对象不存在，请重新打开操作！");
    }
    LambdaQueryWrapper<ArchiveField> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.in(ArchiveField::getId, ids);
    List<ArchiveField> list = archiveFieldMapper.selectList(queryWrapper);
    List<ArchiveField> batchList =
        list.stream()
            .peek(item -> item.setSort(getSort(req, item.getId())))
            .collect(Collectors.toList());
    this.updateBatchById(batchList);
  }

  private int getSort(ArchiveFieldEditSortDTO req, Long id) {
    List<ArchiveFieldFieldSortDTO> fieldSorts =
        req.getFieldSorts().stream()
            .filter(item -> item.getId().equals(id))
            .collect(Collectors.toList());
    return fieldSorts.isEmpty() ? 0 : fieldSorts.get(0).getSort();
  }

  @CacheEvict(allEntries = true)
  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    this.baseMapper.deleteBatchIds(idList);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ArchiveFieldVO view(Long id) {
    ArchiveField entity = archiveFieldMapper.selectById(id);
    return BeanUtil.copyProperties(entity, ArchiveFieldVO.class);
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public ArchiveFieldVO getByFiledKey(ArchiveFieldQueryDTO req) {
    ArchiveField one =
        getOne(
            Wrappers.<ArchiveField>lambdaQuery()
                .eq(ArchiveField::getArchTypeId, req.getArchTypeId())
                .eq(ArchiveField::getTableId, req.getTableId())
                .eq(ArchiveField::getDbFieldKey, req.getDbFieldKey())
                .last(" limit 1"));
    return BeanUtil.copyProperties(one, ArchiveFieldVO.class);
  }

  @CacheEvict(allEntries = true)
  @Override
  public void clearAllCache() {
    log.info("清除数据字典缓存");
  }

  @Cacheable(keyGenerator = "myKeyGenerator")
  @Override
  public List<ArchiveFieldListVO> selectSearchList(List<String> tableList) {
    if (ObjectUtil.isEmpty(tableList)){
      return com.google.common.collect.Lists.newArrayList();
    }
    List<ArchiveField> list =
        list(
            Wrappers.<ArchiveField>query()
                .select("DISTINCT db_field_key as dbFieldKey ")
                .lambda()
                .eq(ArchiveField::getAttrSearch, true)
                .in(ArchiveField::getTableId, tableList));
    return BeanUtil.copyToList(list, ArchiveFieldListVO.class);
  }
}
